//1.结构体的声明

//结构体是一些值的集合，这些值称为成员变量。结构的每个成员可以是不同类型的成员变量
// #include<stdio.h>
// struct stu//前面的结构体关键字，后面的是结构体标签，这两放在一起叫结构体类型    可以定义在主函数的外面
// {
//     char name[20];
//     short age;
//     char phone[13];
//     char sex[5];
// }s1,s2,s3;//如果在这里写上变量，那么这些变量就是全局变量（没在主函数内定义）》》》int a,b,c;一样的，除开s123，上面的所有称作结构体类型   但是少去创建


//（1）结构体常见的使用方法
// typedef struct stu
// {
//         char name[20];
//     short age;
//     char phone[13];
//     char sex[5];
// }stu;//还可以结和typedef这种方法定义下面的结构体变量    stu s;,详细参考自定义类型，这个stu就不是创建的全局变量了，而是重新命名的类型名字

// int main()
// {
//     struct stu s;//上面创建的类型并不占用内存的空间，但是这个s是创建出来的结构体变量占用内存空间
// }










//2.结构体变量的定义和初始化

//结构体成员可以是变量，数组，指针，甚至是其他结构体
// struct point
// {
//     int x;
//     int y;
// }p1;     //这里p1和下面两种算是三种不同的定义结构体变量的方式
// struct point p2;
// struct point p3={1,2};//此处的p3不但定义了，而且进行了值的初始化。如果里面有数组类型的及得用双引号起来{”张三“,"1237823781","shajdasjd"}之类的

// #include<stdio.h>
// struct S
// {
//     int a;
//     char c;
//     char arr[20];
//     double d;
// };

// struct T
// {
//     char ch[10];
//     struct S s;//也就是结构体内能创建结构体变量成员
//     char *pc;
// };

// int main()
// {
//     char arr[]={"hello bit\n"};
//     struct T t={"hehe",{100,'h',"hello word",3.14},arr};//结构体里面的结构体变量需要再次用大括号去进行初始化
//     printf("%s\n",t.ch);//打印结构体成员变量的值就是 结构体变量名.成员变量名
//     printf("%s\n",t.s.arr);//对于成员变量还是结构体的要多加上一个.去使用
//     printf("%lf\n",t.s.d);
//     printf("%s\n",arr);
//     return 0;
// }









//3.结构体传参
//自定义函数的形参是结构体类型的应该怎么办
// typedef struct stu
// {
//     char name[20];
//     short age;
//     char phone[13];
//     char sex[5];
// }stu;

//(1)传值
// void prin1(stu x)//参数是stu类型的结构体定义的变量，形参就得用stu类型的结构体，这个是传值的方式进行输出
// //实参传递过来的数据，形参需要开辟空间进行重新拷贝，如果原参数占用空间比较大，则会造成较大的资源浪费
// {
//     printf("%s\n",x.name);
//     printf("%d\n",x.age);
//     printf("%s\n",x.phone);
//     printf("%s\n",x.sex);
// }
//传值的时候可能会因为变量过大而出现栈溢出的情况


//(2)传址
// void prin2(stu* ps)//这种安全并且效率高。因为不用去拷贝形参的值，只需要接收地址的四个字节
// {
//     printf("%s\n",ps->name);//传递的参数是结构体的地址，那么通过地址打印出实参变量   指针变量名->成员变量名
//     printf("%d\n",ps->age);
//     printf("%s\n",ps->phone);//->指向操作符，自带解引用。但是只能在结构体、联合体、共用体中才能使用。  -----最经常在结构体中使用
//     printf("%s\n",ps->sex);
// }

// int main()
// {
//     stu s={"张三",40,"12222222222","男"};
//     prin1(s);//将结构体变量当做参数进行传参
//     prin2(&s);//将结构体变量的地址当做参数传递过去
//     return 0;
// }//函数传参的时候，参数是需要压栈的。如果传递一个结构体对象的时候，结构体过大，参数压栈的系统开销比较大，所以会导致性能的下降。结构体传参的时候，要传结构体的地址
//栈可以理解为演算纸，做某道题都要存在演算纸，做完了演算纸才报废，如果这道题演算纸写不下了，也就是栈溢出。那么就会出错




//4.关于传参过程中的压栈的补充
// int add(int x,int y)
// {
//     int z=0;;
//     z=x+y;
//     return x+y;
// }
// int main()
// {
//     int a=10;
//     int b=20;
//     int ret=0;
//     ret=add(a,b);//这里就涉及到了add函数的传参          -----任何一次函数的调用都会在内存上的栈区申请空间，这个
// }



//补充：结构体就是个类型，所有可以创建这个类型的数组，这个类型的指针，这个类型的等等；

//结构体指针变量输出的时候一般是这样（*p).a,解引用的时候一定要带上括号，因为.的优先级比*高        或者说p->a

//结构体变量在声明的时候，就分配了空间。结构体和数组不同，结构体类型的变量名并不能直接当作地址使用，这一点和基本数据类型相同。需要对结构体名使用取址符
//&才能进行地址操作，并且取址所得到地址代表的是指向结构体的指针，只是在数据上和结构体中的首元素地址相同。
//也就是说变量类型中，除了数组的名字代表地址，其他都得用取地址符号

//补充：
//#include<stdio.h>
//
//struct S1
//{
//    char c1;
//    char c2;//结构体变量换S1和S2的标签不一样  所以里面的成员变量的名字是可以相同的
//    int a;
//};
//
//struct S2
//{
//    char c1;
//    char c2;
//    int a;
//};
//int main(void)
//{
//    struct S1 test1;
//    struct S2* ptest1;
//    ptest1=&test1;虽然两个结构体的成员变量全部相同，但是还是不能用S2定义的指针来存放S1定义的变量的地址，因为不是同一个类型
//}